﻿/**
*┌──────────────────────────────────────────────────────────────┐
*│　描    述：SQLite的操作工具类                                                   
*│　作    者：执笔小白                                              
*│　版    本：1.1                                       
*│　创建时间：2021-6-13 15:40:56                            
*└──────────────────────────────────────────────────────────────┘
*┌──────────────────────────────────────────────────────────────┐
*│　命名空间: Uril.SQLHelpers                               
*│　类    名：SQLiteHelper                                     
*└──────────────────────────────────────────────────────────────┘
*/
using System;
using System.Data;
using System.Data.SQLite;
using System.Drawing;

namespace Uril.SQLHelpers
{
    /// <summary>
    /// SQLite的操作工具类
    /// </summary>
    public class SQLiteHelper
    {
        #region 变量
        /// <summary>
        /// 数据库连接
        /// SQLiteConStr:D:\TestDB.db
        /// </summary>
        private static SQLiteConnection conn;

        private static object _osqlLock = new object();
        #endregion 变量

        #region 特有
        /// <summary>
        /// 线程中操作使用的锁
        /// </summary>
        public static object OSqlLock
        {
            get { return _osqlLock; }
            set { _osqlLock = value; }
        }

        ~SQLiteHelper()
        {
            conn.Dispose();
        }
        #endregion 特有

        #region 初始化
        /// <summary>
        /// 初始化数据库链接
        /// </summary>
        /// <param name="dbPath">数据库路径</param>
        public static void Initial(string dbPath)
        {
            _osqlLock = new object();
            conn = new SQLiteConnection("Data Source=" + dbPath + ";Version=3;");  //conn = new SQLiteConnection(@"Data Source=D:\;Version=3;");
        }
        #endregion 初始化

        /* 返回的是受影响的行数
        * ExecuteNonQuery()通常情况下为数据库事务处理的首选，当需要执行插入，删除，修改等操作时，首选ExecuteNonQuery(),不适用于Selete语句，返回永远是-1
        * ExecuteNonQuery()执行成功返回的是一受影响的行数，对于"Create Table"和"Drop Table"语句，返回值是0，
        * ExecuteNonQuery()操作数据时，可以不使用DataSet直接更改数据库中的数据。
        */
        public static int ExecuteNonQuery(string sql, params SQLiteParameter[] ps)
        {
            int count = 0;
            lock (_osqlLock)
            {
                try
                {
                    conn.Open();
                    SQLiteCommand cmd = new SQLiteCommand(sql, conn);
                    if (ps != null)
                    {
                        cmd.Parameters.AddRange(ps);
                    }
                    count = cmd.ExecuteNonQuery();
                }
                catch
                {
                    throw;
                }
                finally
                {
                    if (conn.State == ConnectionState.Open)
                    {
                        conn.Close();
                    }
                }
            }
            return count;
        }

        /* ExecuteScalar()方法也可以用来执行SQL语句，但是executescalar()执行SQL语句后返回值与ExecuteNonQuery()并不相同，
        * ExecuteNonQuery()操作后返回的是一个值，而executescalar()操作后则会返回一个对象（object），
        * 如果执行的SQL语句是查询语句，则返回结果是查询后的第一行第一列，
        * 如果执行的SQL语句不是一个查询语句，则会返回一个未实例化的对象，必须通过类型装换来显示。
        * 
        * executescalar()经常使用当需要返回单一值时的情况。例如当插入一条数据信息时，
        * 需要马上知道刚才插入的值，则可以使用executescalar()方法。
        */
        public static object ExecuteScalar(string sql, params SQLiteParameter[] ps)
        {
            lock (_osqlLock)
            {
                try
                {
                    using (SQLiteCommand cmd = new SQLiteCommand(sql, conn))
                    {
                        if (ps != null)
                        {
                            cmd.Parameters.AddRange(ps);
                        }
                        conn.Open();//打开数据库

                        object cc = cmd.ExecuteScalar();
                        conn.Close();
                        return cc;
                    }
                }
                catch
                {
                    throw;
                }
                finally
                {
                    if (conn.State == ConnectionState.Open)
                    {
                        conn.Close();
                    }
                }
            }
        }

        /* 快速查询
         * ExecuteReader比DataSet而言，DataReader具有较快的访问能力，并且能够使用较少的服务器资源。
         * DataReader对象提供了游标形式的读取方式，当从结果行中读取了一行，则游标会继续读取到下一行。
         * 通过read方法可以判断数据是否还有下一行，如果存在数据，则继续运行返回true，如果没有数据，则返回false。
         */
        public static DataTable ExecuteReader(string sql, params SQLiteParameter[] ps)
        {
            lock (_osqlLock)
            {
                try
                {
                    using (SQLiteCommand cmd = new SQLiteCommand(sql, conn))
                    {
                        if (ps != null)
                        {
                            cmd.Parameters.AddRange(ps);
                        }
                        conn.Open();
                        SQLiteDataReader reader = cmd.ExecuteReader();
                        DataTable datatable = new DataTable();

                        // 添加DataTable列
                        for (int i = 0; i < reader.FieldCount; i++)
                        {
                            DataColumn myDataColumn = new DataColumn();
                            myDataColumn.DataType = reader.GetFieldType(i);
                            myDataColumn.ColumnName = reader.GetName(i);
                            datatable.Columns.Add(myDataColumn);
                        }

                        // 添加DataTable数据
                        while (reader.Read())
                        {
                            //var n1 = reader.GetInt32(0);   // 第一列值
                            //var n2 = reader.GetString(1);  // 第二列值

                            DataRow myDataRow = datatable.NewRow();
                            for (int i = 0; i < reader.FieldCount; i++)
                            {
                                myDataRow[i] = reader[i].ToString();
                            }
                            datatable.Rows.Add(myDataRow);
                        }

                        reader.Close();
                        //con.Open();
                        cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);  // 关闭关联的connection

                        return datatable;
                    }
                }
                catch
                {
                    throw;
                }
                finally
                {
                    if (conn.State == ConnectionState.Open)
                    {
                        conn.Close();
                    }
                }
            }
        }

        /* 通用查询方法
        * 为了将数据库的数据填充到dataset中，则必须先使用adapter对象的方法实现填充，当数据填充完成后，
        * 开发人员可以将记录添加到dataset对象中，然后使用update方法将数据插入到数据库中。
        */
        public static DataSet SqlDataAdapter_Fill(string sql, params SQLiteParameter[] ps)  // 动态添加SQLiteParameter[]不方便,所以用list
        {
            DataSet dsr = new DataSet();
            lock (_osqlLock)
            {
                try
                {
                    DataSet ds = new DataSet();
                    conn.Open();
                    SQLiteDataAdapter adapter = new SQLiteDataAdapter(sql, conn);
                    if (ps != null)
                    {
                        adapter.SelectCommand.Parameters.AddRange(ps);
                    }
                    adapter.Fill(ds);
                    dsr = ds;
                }
                catch
                {
                    throw;
                }
                finally
                {
                    if (conn.State == ConnectionState.Open)
                    {
                        conn.Close();
                    }
                }
            }
            return dsr;
        }

        /* 使用DataSet数据集更新数据
         * 为了将数据库的数据填充到dataset中，则必须先使用adapter对象的方法实现填充，
         */
        public static int SqlDataAdapter_Update(string sql, DataSet ds, string tableName)
        {
            int count = 0;
            lock (_osqlLock)
            {
                try
                {
                    conn.Open();
                    using (SQLiteDataAdapter sa = new SQLiteDataAdapter(sql, conn))
                    {
                        SQLiteCommandBuilder builder = new SQLiteCommandBuilder(sa);
                        count = sa.Update(ds, tableName);
                    }
                }
                catch
                {
                    throw;
                }
                finally
                {
                    if (conn.State == ConnectionState.Open)
                    {
                        conn.Close();
                    }
                }
            }
            return count;
        }

        #region 事务-bug
        /// <summary>
        /// 返回的是受影响的行数-事务
        /// </summary>
        /// <param name="sql">多个sql</param>
        /// <param name="ps">指定类型的参数</param>
        /// <returns></returns>
        public static int ExecuteNonQueryTrans(string sql, params SQLiteParameter[] ps)
        {
            lock (_osqlLock)
            {
                conn.Open();
                using (SQLiteTransaction trans = conn.BeginTransaction())
                {
                    try
                    {
                        using (SQLiteCommand cmd = new SQLiteCommand())
                        {
                            cmd.Connection = conn;
                            cmd.Transaction = trans;
                            cmd.CommandText = sql;
                            if (ps != null)
                            {
                                cmd.Parameters.AddRange(ps);
                            }
                            int cc = cmd.ExecuteNonQuery();

                            trans.Commit();
                            conn.Close();
                            return cc;
                        }
                    }
                    catch (Exception ex)
                    {
                        trans.Rollback();
                        conn.Close();
                        conn.Dispose();
                        throw ex;
                    }
                }
            }
        }
        #endregion

        #region 扩展
        /// <summary>
        /// 数据库查询的方法-扩展
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <returns>数据表DataTable</returns>
        public static DataTable SqlDataAdapter(string sql)
        {
            DataTable dt = new DataTable();
            lock (_osqlLock)
            {
                try
                {
                    DataSet ds = new DataSet();
                    conn.Open();
                    SQLiteDataAdapter adapter = new SQLiteDataAdapter(sql, conn);
                    adapter.Fill(ds);
                    if (ds.Tables.Count > 0)
                    {
                        dt = ds.Tables[0];
                    }
                }
                catch
                {
                    throw;
                }
                finally
                {
                    if (conn.State == ConnectionState.Open)
                    {
                        conn.Close();
                    }
                }
            }
            return dt;
        }
        /// <summary>
        /// 查询数据条数
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public static int SelectDataRowCount(string sql)
        {
            try
            {
                return SqlDataAdapter(sql).Rows.Count;
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// 判断数据库中是否存在某表
        /// </summary>
        /// <param name="TableName">表名</param>
        /// <returns>是否存在</returns>
        public static bool ExistTable(string TableName)
        {
            bool IsExist = false;
            try
            {
                string sql = "select count(*) from MSysObjects WHERE MSysObjects.Name Like '" + TableName + "'";
                DataTable dt = SqlDataAdapter(sql);
                if (dt.Rows.Count != 0)
                {
                    if (dt.Rows[0].ItemArray[0].ToString().IndexOf('1') > -1)
                    {
                        IsExist = true;
                    }
                }
            }
            catch
            {
                IsExist = false;
            }
            return IsExist;
        }
        #endregion
    }
}